﻿/*******************************************************************************
Copyright (c) 2011-2013, PKStudio
Copyright (c) 2008-2009, Jason Booth

http://pkstudio.org

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
of the Software, and to permit persons to whom the Software is furnished to do
so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*******************************************************************************/


using System;

namespace PKEngine
{
	/// <summary>
	/// Base class for Easing actions.
	/// </summary>
	public class PKActionEase : PKActionInterval
	{
		#region Private Fields
		protected PKActionInterval innerAction;
		#endregion

		#region Initialization
		public PKActionEase(PKActionInterval action)
			: base(action.Duration)
		{
			this.innerAction = action;
		}
		#endregion

		#region Public Methods
		public override void StartWithTarget(PKNode target)
		{
			base.StartWithTarget(target);
			innerAction.StartWithTarget(target);
		}

		public override void Stop()
		{
			innerAction.Stop();
			base.Stop();
		}

		public override void Update(float time)
		{
			innerAction.Update(time);
		}
		#endregion
	}

	/// <summary>
	/// Base class for Easing actions with rate parameters.
	/// </summary>
	public class PKEaseRateAction : PKActionEase
	{
		#region Public Properties
		/// <summary>
		/// Rate.
		/// </summary>
		public float Rate { get; set; }
		#endregion

		#region Initialization
		public PKEaseRateAction(PKActionInterval action, float rate)
			: base(action)
		{
			this.Rate = rate;
		}
		#endregion
	}

	/// <summary>
	/// EaseIn action with a rate.
	/// </summary>
	public class PKEaseIn : PKEaseRateAction
	{
		#region Initialization
		public PKEaseIn(PKActionInterval action, float rate)
			: base(action, rate)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			innerAction.Update((float)Math.Pow(time, Rate));
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseOut(innerAction.Reverse() as PKActionInterval, Rate);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseIn(innerAction.Copy() as PKActionInterval, Rate);
		}
		#endregion
	}

	/// <summary>
	/// EaseOut action with a rate
	/// </summary>
	public class PKEaseOut : PKEaseRateAction
	{
		#region Initialization
		public PKEaseOut(PKActionInterval action, float rate)
			: base(action, rate)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			innerAction.Update((float)Math.Pow(time, 1 / Rate));
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseIn(innerAction.Reverse() as PKActionInterval, Rate);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseOut(innerAction.Copy() as PKActionInterval, Rate);
		}
		#endregion
	}

	/// <summary>
	/// EaseInOut action with a rate
	/// </summary>
	public class PKEaseInOut : PKEaseRateAction
	{
		#region Initialization
		public PKEaseInOut(PKActionInterval action, float rate)
			: base(action, rate)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			time *= 2;
			if (time < 1)
			{
				innerAction.Update(0.5f * (float)Math.Pow(time, Rate));
			}
			else
			{
				innerAction.Update(1 - 0.5f * (float)Math.Pow(2 - time, Rate));
			}
		}

		public override PKFiniteTimeAction Reverse()
		{
			// InOut and OutIn are symmetrical.
			return new PKEaseInOut(innerAction.Reverse() as PKActionInterval, Rate);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseInOut(innerAction.Copy() as PKActionInterval, Rate);
		}
		#endregion
	}

	/// <summary>
	/// Ease Exponential In.
	/// </summary>
	public class PKEaseExponentialIn : PKActionEase
	{
		#region Initialization
		public PKEaseExponentialIn(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			if (time == 0)
			{
				innerAction.Update(0);
			}
			else
			{
				innerAction.Update((float)Math.Pow(2, 10 * (time - 1)) - 0.001f);
			}
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseExponentialOut(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseExponentialIn(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}

	/// <summary>
	/// Ease Exponential Out.
	/// </summary>
	public class PKEaseExponentialOut : PKActionEase
	{
		#region Initialization
		public PKEaseExponentialOut(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			if (time == 1)
			{
				innerAction.Update(1);
			}
			else
			{
				innerAction.Update((float)(1 - Math.Pow(2, -10 * time)));
			}
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseExponentialIn(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseExponentialOut(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}

	/// <summary>
	/// Ease Exponential InOut.
	/// </summary>
	public class PKEaseExponentialInOut : PKActionEase
	{
		#region Initialization
		public PKEaseExponentialInOut(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			if (time == 0)
			{
				innerAction.Update(0);
			}
			else if (time == 1)
			{
				innerAction.Update(1);
			}
			else
			{
				time *= 2;
				if (time < 1)
				{
					innerAction.Update(0.5f * (float)Math.Pow(2, 10 * (time - 1)));
				}
				else
				{
					innerAction.Update(0.5f * (float)(2 - Math.Pow(2, -10 * (time - 1))));
				}
			}
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseExponentialInOut(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseExponentialInOut(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}

	/// <summary>
	/// Ease Sine In.
	/// </summary>
	public class PKEaseSineIn : PKActionEase
	{
		#region Initialization
		public PKEaseSineIn(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			innerAction.Update(1 - (float)Math.Cos(time * Math.PI / 2));
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseSineOut(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseSineIn(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}

	/// <summary>
	/// Ease Sine Out.
	/// </summary>
	public class PKEaseSineOut : PKActionEase
	{
		#region Initialization
		public PKEaseSineOut(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			innerAction.Update((float)Math.Sin(time * Math.PI / 2));
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseSineIn(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseSineOut(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}

	/// <summary>
	/// Ease Sine InOut.
	/// </summary>
	public class PKEaseSineInOut : PKActionEase
	{
		#region Initialization
		public PKEaseSineInOut(PKActionInterval action)
			: base(action)
		{
		}
		#endregion

		#region Public Methods
		public override void Update(float time)
		{
			innerAction.Update(0.5f * (float)(1 - Math.Cos(time * Math.PI)));
		}

		public override PKFiniteTimeAction Reverse()
		{
			return new PKEaseSineInOut(innerAction.Reverse() as PKActionInterval);
		}

		public override PKFiniteTimeAction Copy()
		{
			return new PKEaseSineInOut(innerAction.Copy() as PKActionInterval);
		}
		#endregion
	}
}
